<template>
  <div class="containter">
    <a-upload
      :file-list="fileList"
      :max-count="1"
      :before-upload="beforeUpload"
      @remove="handleRemove"
    >
      <a-button>
        <upload-outlined></upload-outlined>
        选择文件
      </a-button>
      <template #itemRender="{ file, actions }">
        <a-row>
          <a-space>
            <!-- 附件图标 -->
            <PaperClipOutlined />
            <span :style="file.status === 'error' ? 'color: red' : ''">{{
              file.name
            }}</span>
            <!-- 上传按钮图标 -->
            <UploadOutlined v-if="uploadingCom" @click="handleUpload" />
            <!-- 上传中图标 -->
            <LoadingOutlined v-if="uploading" />
            <!-- 删除按钮图标 -->
            <DeleteOutlined @click="handleRemove"></DeleteOutlined>
          </a-space>
        </a-row>
        <a-row>
          <a-progress :percent="precent" size="small" />
        </a-row>
      </template>
    </a-upload>
  </div>
</template>
<script setup>
import { ref, computed } from "vue";
import {
  UploadOutlined,
  DeleteOutlined,
  PaperClipOutlined,
  LoadingOutlined,
} from "@ant-design/icons-vue";
import { calculateFileMd5ByDefaultChunkSize } from "@/utils/mixin";
import {
  checkFileByMd5,
  uploadChunk,
  checkChunk,
  mergeChunks,
} from "@/api/file";

// 文件列表
const fileList = ref([]);
const precent = ref(0); // 上传进度
// 分片大小
const chunkSize = parseInt(import.meta.env.VITE_CHUNK_SIZE);
// 是否上传中
const uploading = ref(false);
const uploadingCom = computed(() => !uploading.value);

// 上传文件之前的钩子，参数为上传的文件, 返回 false 则停止上传
function beforeUpload(file) {
  fileList.value = [file];
  precent.value = 0;
  return false; // 返回 false 则停止上传
}

// 点击删除文件按钮时的钩子，返回 false 则停止删除
function handleRemove() {
  fileList.value = [];
}

/**
 * 上传文件
 */
async function handleUpload() {
  uploading.value = true;
  console.log("上传中...", fileList.value[0]);
  // 计算 MD5
  const chunkFiles = [];
  const md5 = await calculateFileMd5ByDefaultChunkSize(
    fileList.value[0],
    chunkSize,
    chunkFiles
  );
  // // 验证文件是否已经上传过
  console.log("md5: ", md5);
  console.log("chunkFiles: ", chunkFiles);

  checkFileByMd5(md5).then((res) => {
    if (res.code == 200) {
      // 文件已经上传过
      console.log("文件已经上传过", res);
      precent.value = 100;
      uploading.value = false;
    } else {
      // 文件没有上传过
      console.log("文件没有上传过");
      // 上传文件
      uploadFile(md5, chunkFiles);
    }
  });
}

function uploadFile(md5, chunkFiles) {
  const file = fileList.value[0];
  const fileName = file.name;
  checkChunk(md5, fileName).then((res) => {
    // res 是所有的已经上传的分片
    console.log("已有分片: ", res.data);

    res.data = res.data.map((item) => parseInt(item.chunkIndex));
    if (res.code === 200) {
      // 上传分片
      uploadChunks(md5, fileName, chunkFiles, res.data);
    }
  });
}

function uploadChunks(md5, fileName, chunkFiles, skipChunks) {
  const fileSize = fileList.value[0].size;
  let count = 0;
  const uploadChunks = chunkFiles.filter((chunkFile, index) => {
    // 修改进度条百分比
    precent.value += Math.round((1 / chunkFiles.length) * 100);
    return !skipChunks.includes(index);
  });
  const total = uploadChunks.length;
  if (uploadChunks.length === 0) {
    // 上传完成
    merge(md5);
    return;
  } else {
    uploadChunks.forEach((chunkFile, index) => {
      uploadChunk(chunkFile, {
        md5,
        fileName,
        index,
        chunkSize,
        fileSize,
        totalChunks: chunkFiles.length,
      }).then((res) => {
        if (res.code === 200) {
          // 修改进度条百分比
          precent.value += (1 / chunkFiles.length) * 100;
          count++;
          console.log("上传分片成功", index);
          if (count === total) {
            // 上传完成
            console.log("所有分片上传完成");
            merge(md5);
          }
        }
      });
    });
  }
}

function merge(iderntifier) {
  // 合并分片
  mergeChunks({ iderntifier }).then((res) => {
    if (res.code === 200) {
      uploading.value = false;
      console.log("合并分片成功", res.data);
      // 上传成功
    }
  });
}
</script>

<style scoped>
.containter {
  width: 400px;
}
</style>
